//
//  Pedometer.swift
//  HSMainViewControllerHandler
//
//  Created by haisheng huang on 16/7/21.
//  Copyright © 2016年 haisheng huang. All rights reserved.
//

import Foundation
import UIKit
import CoreMotion
import AVFoundation


let kStepCountInfo: String = "com.haisheng.kStepCountInfo"
let kStepCount: String = "com.haisheng.kStepCount"
let kStepTime: String = "com.haisheng.kStepTime"
let kStepState: String = "com.haisheng.kStepState"
let kStepDate: String = "com.haisheng.kStepDate"

let StepFastTime: Double = 0.2
let StepNormalTime: Double = 1.0

@objc open class Pedometer: NSObject {

    struct Static {
        static var onceToken: Int = 0
        static var instance: Pedometer? = nil
    }
    
    private static var __once: () = {
            Static.instance = Pedometer()
        }()

    var isCounting: Bool?
    var stepFlag: Bool?
    var stepCount: NSInteger?
    var elapsedTime: TimeInterval?
    var countTimer: Timer?
    var filter: AccelerometerFilter?
    var weakShakes: NSInteger?
    var lastShakeTime: TimeInterval?
    var motionManager: CMMotionManager?
    var workQueue: OperationQueue?
    var updateFrequency: Double? = 60.0
    var delegate: PedometerDelegate?
    
    fileprivate var player: AVAudioPlayer?
    
    class var sharedInstance: Pedometer {
       
        _ = Pedometer.__once
        
        return Static.instance!
    }
    
    override init() {
        super.init()
        
        filter = LowpassFilter.init(sampleRate: updateFrequency!, cutoffFrequency: 5.0)
        filter?.adaptive = true
        
        motionManager = CMMotionManager()
        motionManager?.accelerometerUpdateInterval = 1 / updateFrequency!
        workQueue = OperationQueue()
        
    }
    
    func counter(_ data: CMAccelerometerData) -> Void {
        
        let acc: CMAcceleration = data.acceleration
        let bShake: Bool = (fabs(acc.x) > 1.2 || fabs(acc.y) > 1.2 || fabs(acc.z) > 1.2)
        
        if bShake == true {
            let diff: TimeInterval = data.timestamp - lastShakeTime!;
            if diff >= StepFastTime && diff <= StepNormalTime {
                stepCount = stepCount! + 1
                self.updateState()
                self.notifyDelegate()
                weakShakes = 0
                self.performSelector(onMainThread: #selector(self.beginTracePedometerTime), with: nil, waitUntilDone: true)
            }
            lastShakeTime = data.timestamp
        } else {
            let bWeakShake: Bool = (fabs(acc.x) < 1.0 && fabs(acc.y) < 1.0 && fabs(acc.z) < 1.0)
            if bWeakShake == true {
                weakShakes! += 1
                if weakShakes == 300 {
                    weakShakes = 300
                    self.performSelector(onMainThread: #selector(self.endTracePedometerTime), with: nil, waitUntilDone: true)
                }
            }
        }
        
    }
    
    func notifyDelegate() -> Void {
        
        if delegate != nil && delegate!.responds(to: #selector(delegate?.pedometer(_:stepsCount:))) {
            DispatchQueue.main.async(execute: {
                self.delegate!.pedometer!(self, stepsCount: self.stepCount!)
                
            })
        }
    }
    
    // Mark: 计步器状态
    func updateState() -> Void {
        //do something
    }
    
    func saveCounterState() -> Void {
        self.updateState()
        
        let info:NSDictionary = [kStepCount:stepCount!,
                                 kStepTime:elapsedTime!,
                                 kStepState:isCounting!,
                                 kStepDate:self.getCurrentDateString()]
        
        UserDefaults.standard.set(info, forKey: kStepCountInfo)
        
    }
    
    func getCounterState() -> NSDictionary {
        let counterInfo: NSDictionary = UserDefaults.standard.object(forKey: kStepCountInfo)as! NSDictionary
        return counterInfo
    }
    
    //恢复计步器的状态为最后一次记录的状态
    func recoverState() -> Void {
        let dict: NSDictionary = self.getCounterState()
        var isRun: Bool = false
        
        if dict[kStepDate] as! String == self.getCurrentDateString() {
            
            stepCount = (dict[kStepCount] as AnyObject).intValue
            elapsedTime = (dict[kStepTime] as AnyObject).doubleValue
            isRun = ((dict[kStepState] as AnyObject).boolValue)!
        } else {
            if dict[kStepDate] as! String == "" {
                return
            }
            
            //重置数据
            stepCount = 0
            elapsedTime = 0
            isRun = false
            let info:NSDictionary = [kStepCount:stepCount!,
                                     kStepTime:elapsedTime!,
                                     kStepState:isCounting!,
                                     kStepDate:self.getCurrentDateString()]
            
            UserDefaults.standard.set(info, forKey: kStepCountInfo)
            
        }
        
        if isRun == true {
            self.startCount()
        } else {
            self.isCounting = isRun
        }
    }
    
    //计算需隔多少步 保存一次计步器的状态 此间隔随着步数的增加而增加 但不会超过50步
    func theIntervalToSaveState() -> NSInteger {
        var interval = stepCount! / 100 + 5
        interval = (interval > 50) ? 50: interval
        return interval
    }
    
    // MARK: 开始计步
    func startCount() -> Void {
        
        if isCounting == true {
            return
        }
        
        if motionManager?.isAccelerometerAvailable == false {
            print("设备无运动传感器")
            return
        }
        
        if motionManager?.isAccelerometerActive == true {
            //获取设备运动信息的服务 正在运行 先停掉
            motionManager?.stopAccelerometerUpdates()
        }
        
        isCounting = true
        unowned let weakSelf = self
        motionManager?.startAccelerometerUpdates(to: workQueue!, withHandler: { (accelerometerData, error) in
            weakSelf.counter(accelerometerData!)
        })
        
        self.startBackgroundTask()
    }
    
    //暂停计步
    func pauseCount() -> Void {
        
        isCounting = false
        
        if motionManager?.isAccelerometerActive == true {
            motionManager?.stopAccelerometerUpdates()
        }
        
        // 暂停计步，停止计步定时器
        self.endTracePedometerTime()
        // 暂停或停止计步时, 保存计步器状态
        self.saveCounterState()
    }
    
    // 停止计步
    func stopCount() -> Void {
        self.pauseCount()
        self.stopBackgroundTask()
        self.reset()
    }
    
    // 复位计步器
    func reset() -> Void {
        stepCount = 0
        elapsedTime = 0
        self.saveCounterState()
    }
    
    func analyzeWalk(_ x: UIAccelerationValue, y: UIAccelerationValue, z: UIAccelerationValue) -> Void {
        let hiThreshold: UIAccelerationValue = 1.1
        let lowThreshold: UIAccelerationValue = 0.99
        var composite: UIAccelerationValue
        composite = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
        
        if stepFlag == true {
            
            if composite < lowThreshold {
                stepCount! += 1
                stepFlag = false
                self.updateState()
                
                if delegate != nil && delegate?.responds(to: #selector(PedometerDelegate.pedometer(_:stepsCount:))) == true {
                    DispatchQueue.main.async(execute: { 
                        self.delegate?.pedometer!(self, stepsCount: self.stepCount!)
                        if (self.stepCount! % self.theIntervalToSaveState() == 0) {
                            self.saveCounterState()
                        }
                    })
                }
            }
            
        } else {
            if composite > hiThreshold {
                stepFlag = true
            }
        }
    }
    
    // MARK: 记录计步有效时间
    func beginTracePedometerTime() -> Void {
        if countTimer!.isValid == true {
            return
        }
        countTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.timetick(_:)), userInfo: nil, repeats: true)
    }
    
    func endTracePedometerTime() -> Void {
        if countTimer?.isValid == true {
            countTimer?.invalidate()
        }
    }
    
    func timetick(_ timer: Timer) -> Void {
        elapsedTime! += 1;
    }
    
    // MARK: 后台任务
    fileprivate func startBackgroundTask() -> Void {
        self.playAudio()
    }
    
    fileprivate func stopBackgroundTask() -> Void {
        self.stopAudio()
    }
    
    // MARK: 音频实现后台运行相关处理
    fileprivate func silentAudio() -> URL {
        
        let bytes: [UInt8] = [0x52, 0x49, 0x46, 0x46, 0x26, 0x0, 0x0, 0x0, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x44, 0xac, 0x0, 0x0, 0x88, 0x58, 0x1, 0x0, 0x2, 0x0, 0x10, 0x0, 0x64, 0x61, 0x74, 0x61, 0x2, 0x0, 0x0, 0x0, 0xfc, 0xff]
        let data: Data = Data(bytes: UnsafePointer<UInt8>(bytes), count:bytes.count)
        let audioPath: String = ((NSHomeDirectory() + "/Library") + "/Caches") + "/silent.wav"
        let fileManager: FileManager = FileManager.default
        var isDirectory: ObjCBool = ObjCBool(false)
        if fileManager.fileExists(atPath: audioPath, isDirectory: &isDirectory) == true {
            //已存在该文件了
        } else {
            try? data.write(to: URL(fileURLWithPath: audioPath), options: [.atomic])
        }
        let audioPathURL: URL = URL(fileURLWithPath: audioPath)
        return audioPathURL
    }
    
    fileprivate func playAudio() -> Void {
        DispatchQueue.main.async { 
            //保证音乐播放操作是在主线程中
            self.stopAudio()
            
            // 设置音频中断回调 i.e 电话打进来了
            NotificationCenter.default.addObserver(self, selector: #selector(self.audioInterrupted(_:)), name: NSNotification.Name.AVAudioSessionInterruption, object: nil)
            
            // 设置音频类别 自身播放时 允许其他APP播放音频
            do {
                try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.mixWithOthers)
                
            } catch {
                print("play error:\(error)")
            }
            
            // 激活
            do {
                try AVAudioSession.sharedInstance().setActive(true)
            } catch {
                print("AVAudioSession set active error:\(error)")
            }
            
            let audioUrl: URL = self.silentAudio()
            do {
                try self.player = AVAudioPlayer(contentsOf: audioUrl)
            } catch {
                print("player init failure")
            }
            
            self.player?.volume = 0.0
            self.player?.numberOfLoops = -1 //重复播放
            self.player?.prepareToPlay()
            self.player?.play()
            
        }
    }
    
    fileprivate func stopAudio() -> Void {
        
        if self.player?.isPlaying == true {
            self.player?.stop()
        }
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVAudioSessionInterruption, object: nil)
    }
    
    //iOS之后，音频中断，通过Notification
    @objc fileprivate func audioInterrupted(_ notification: Notification) -> Void {
        
        if (notification as NSNotification).userInfo!["AVAudioSessionInterruptionTypeKey"] as! AVAudioSessionInterruptionType == AVAudioSessionInterruptionType.began {
            // 音频中断开始
            self.playAudio()
        }
    }
    
    //获取当前日期字符串
    func getCurrentDateString() -> String {
        let dateFormatter: DateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let currentDate: String = dateFormatter.string(from: Date())
        return currentDate
    }
}


@objc public protocol PedometerDelegate: NSObjectProtocol {

    @objc optional func pedometer(_ pedometer:Pedometer, stepsCount: NSInteger) -> Void
    
}


